package cn.xx.controller;

import cn.xx.controller.viewobject.UserVO;
import cn.xx.error.BusinessException;
import cn.xx.error.EmBusinessError;
import cn.xx.response.CommonReturnType;
import cn.xx.service.UserService;
import cn.xx.service.model.UserModel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * DEFAULT_ALLOWED_HEADERS：
 * 允许跨域传输所有的header参数，将用于使用token放入header域做session共享的跨域请求
 *
 * @author xiexu
 * @create 2022-04-19 21:05
 */
@Controller("user")
@RequestMapping("/user")
// 跨域请求中，不能做到session共享，需要
@CrossOrigin(origins = {"*"}, allowCredentials = "true", allowedHeaders = "*")
public class UserController extends BaseController {

    @Autowired
    private UserService userService;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private RedisTemplate redisTemplate;

    // 用户登录接口
    @RequestMapping(value = "/login", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType login(@RequestParam(name = "telphone") String telphone, @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        // 入参校验
        if (org.apache.commons.lang3.StringUtils.isEmpty(telphone) || org.apache.commons.lang3.StringUtils.isEmpty(password)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        // 用户登录服务，用来校验用户登录是否合法
        // 手机号+用户加密后的密码
        UserModel userModel = userService.validateLogin(telphone, this.EncodeByMd5(password));

        // 将登陆凭证加入到用户登录成功的session内

        // 修改成若用户登陆验证成功，将对应的登陆信息和登陆凭证一起存入redis中
        // 生成登录凭证token，一般采用UUID方式
        String uuidToken = UUID.randomUUID().toString();
        // 例如：5dcac225-b98b-4618-b09d-533440b936e7 将token的"-"给去掉
        uuidToken = uuidToken.replace("-", "");

        // this.httpServletRequest.getSession().setAttribute("IS_LOGIN", true);
        // this.httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);

        // 建立token和用户登录态之间的联系
        redisTemplate.opsForValue().set(uuidToken, userModel); //上面两行替换成这行代码
        // 设置uuidToken过期时间为1小时
        redisTemplate.expire(uuidToken, 1, TimeUnit.HOURS);

        // 下发token
        return CommonReturnType.create(uuidToken);
    }

    // 用户注册接口
    @RequestMapping(value = "/register", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType register(@RequestParam(name = "telphone") String telphone, @RequestParam(name = "otpCode") String otpCode, @RequestParam(name = "name") String name, @RequestParam(name = "gender") String gender, @RequestParam(name = "age") String age, @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        // 验证手机号和对应的otpCode是否相符合
        String inSessionOptCode = (String) this.httpServletRequest.getSession().getAttribute(telphone);
        // 用druid的equals内部做了判空处理，如果两个都为空返回true
        if (!com.alibaba.druid.util.StringUtils.equals(otpCode, inSessionOptCode)) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信验证码不符合");
        }
        // 用户的注册流程
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(Integer.valueOf(age));
        userModel.setGender(Byte.valueOf(gender));
        userModel.setTelphone(telphone);
        userModel.setRegisterMode("byphone");
        // 密码加密
        userModel.setEncrptPassword(this.EncodeByMd5(password));
        userService.register(userModel);
        return CommonReturnType.create(null);
    }

    // 密码加密
    public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 确定计算方法
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        BASE64Encoder base64en = new BASE64Encoder();
        // 加密字符串
        String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
        return newstr;
    }

    // 用户获取opt短信接口
    @RequestMapping(value = "/getotp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType getOtp(@RequestParam(name = "telphone") String telphone) {
        // 需要按照一定的规则生成OTP验证码
        Random random = new Random();
        // 此时随机数取值[0,99999)
        int randomInt = random.nextInt(99999);
        // 此时随机数取值[10000,109999)
        randomInt += 10000;
        String optCode = String.valueOf(randomInt);

        // 将OTP验证码同对应用户的手机号关联，使用httpsession的方式绑定手机号与optCode
        httpServletRequest.getSession().setAttribute(telphone, optCode);

        // 将OTP验证码通过短信通道发送给用户，暂时先省略
        System.out.println("telphone = " + telphone + " & optCode = " + optCode);

        return CommonReturnType.create(null);
    }

    @RequestMapping("/get")
    @ResponseBody
    public CommonReturnType getUser(@RequestParam(name = "id") Integer id) throws BusinessException {
        // 调用service服务获取对应id的用户对象并返回给前端
        UserModel userModel = userService.getUserById(id);

        // 若获取的对应用户信息不存在
        if (userModel == null) {
            throw new BusinessException(EmBusinessError.USER_NOT_EXIST);
        }

        // 将核心领域模型用户对象转化为可供UI使用的viewobject
        UserVO userVO = convertFromModel(userModel);
        // 返回通用对象
        return CommonReturnType.create(userVO);
    }

    private UserVO convertFromModel(UserModel userModel) {
        if (userModel == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        // 把对应userModel的属性拷贝到userVO里面
        BeanUtils.copyProperties(userModel, userVO);
        return userVO;
    }

}
